//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.Contracts;
using System.Reflection;
using System.Windows;
using OpenLS.Core.Notification;
using OpenLS.Spreadsheet.Extensibility;
using OpenLS.Spreadsheet.UI.Internals;
using OpenLS.Spreadsheet.UI.Internals.Resources;
using Expression = OpenLS.Spreadsheet.Expressions.Expression;

namespace OpenLS.Spreadsheet.UI
{
    public class AutoFill : ViewModelBase
    {
        private readonly UIElement _element;
        public NavigationDirection Direction;
        private bool isEnabled = true;
        private int Length;
        private bool useIncrement;

        public AutoFill(CellsRange source, UIElement element)
        {
            _element = element;
            Contract.Requires(source != null);
            if (source == null)
                throw new ArgumentNullException("source");
            Source = source;
        }

        public AutoFillOptions Options
        {
            get;
            set;
        }


        public CellsRange Source
        {
            get;
            set;
        }

        public int Column
        {
            get { return TargetRange.Address.TopLeft.Column; }
        }

        public int Row
        {
            get { return TargetRange.Address.TopLeft.Row; }
        }


        public int RowSpan
        {
            get { return TargetRange.RowSpan; }
        }

        public int ColumnSpan
        {
            get { return TargetRange.ColumnSpan; }
        }

        public bool IsEnabled
        {
            get { return isEnabled; }
            set
            {
                isEnabled = value;
                notifyPropertyChanged("IsEnabled");
                notifyPropertyChanged("ColumnSpan");
                notifyPropertyChanged("RowSpan");
                notifyPropertyChanged("Column");
                notifyPropertyChanged("Row");
            }
        }

        public bool UseIncrement
        {
            get { return useIncrement; }
            set { useIncrement = value; }
        }

        public bool HasNaturalIncrement
        {
            get
            {
                switch (Direction)
                {
                    case NavigationDirection.Left:
                    case NavigationDirection.Right:
                        return Source.ColumnSpan > 1;
                    case NavigationDirection.Up:
                    case NavigationDirection.Down:
                        return Source.RowSpan > 1;
                    default:
                        throw new NotSupportedException();
                }
            }
        }

        private CellsRange TargetRange
        {
            get
            {
                if (!IsEnabled)
                    return Source;
                switch (Direction)
                {
                    case NavigationDirection.Right:
                        return CellsRange.Create(Source.Worksheet, new RangeAddress(Source.Address.TopLeft,
                                                                                    Source.Address.BottomRight +
                                                                                    new CellRelativeAddress(0, Length)));
                    case NavigationDirection.Left:
                        return
                            CellsRange.Create(Source.Worksheet,
                                              new RangeAddress(
                                                  Source.Address.TopLeft - new CellRelativeAddress(0, Length),
                                                  Source.Address.BottomRight));
                    case NavigationDirection.Down:
                        return CellsRange.Create(Source.Worksheet, new RangeAddress(Source.Address.TopLeft,
                                                                                    Source.Address.BottomRight +
                                                                                    new CellRelativeAddress(Length, 0)));
                    case NavigationDirection.Up:
                        return
                            CellsRange.Create(Source.Worksheet,
                                              new RangeAddress(
                                                  Source.Address.TopLeft - new CellRelativeAddress(Length, 0),
                                                  Source.Address.BottomRight));
                    default:
                        throw new NotSupportedException();
                }
            }
        }


        public void SetTarget(NavigationDirection p, int length)
        {
            IsEnabled = true;
            Direction = p;
            Length = length;
            notifyPropertyChanged("ColumnSpan");
            notifyPropertyChanged("RowSpan");
            notifyPropertyChanged("Column");
            notifyPropertyChanged("Row");
        }

        public void Apply()
        {
            if (this.TargetRange.IsProtected)
            {
                SpreadsheetElement.OnProtectionViolation(_element);
                return;
            }
            if (!IsEnabled)
                return;
            using (Source.Worksheet.CreateUndoScope(UIStringTable.AutoFill))
            {
                if (Options != AutoFillOptions.NoFormatting)
                    ApplyFormatting();
                if (Options != AutoFillOptions.FormattingOnly)
                    applyAutoFill();
                Source.Worksheet.Selection = (TargetRange);
            }
        }

        private void ApplyFormatting()
        {
            switch (Direction)
            {
                case NavigationDirection.Up:
                case NavigationDirection.Down:

                    for (int column = Source.Address.TopLeft.Column;
                         column <= Source.Address.BottomRight.Column;
                         column++)
                    {
                        ApplyFormatting(
                            Source.Worksheet[new CellAddress(Source.Address.TopLeft.Row, column)],
                            Source.Worksheet[new CellAddress(Source.Address.BottomRight.Row, column)]);
                    }
                    break;
                case NavigationDirection.Left:
                case NavigationDirection.Right:

                    for (int row = Source.Address.TopLeft.Row; row <= Source.Address.BottomRight.Row; row++)
                    {
                        ApplyFormatting(
                            Source.Worksheet[new CellAddress(row, Source.Address.TopLeft.Column)],
                            Source.Worksheet[new CellAddress(row, Source.Address.BottomRight.Column)]
                            );
                    }
                    break;
            }
        }

        private void ApplyFormatting(Cell baseCell, Cell lastCell)
        {
            Cell cell = lastCell;
            int incr;
            switch (Direction)
            {
                case NavigationDirection.Right:
                case NavigationDirection.Down:
                    incr = 1;
                    break;
                default:
                    incr = -1;
                    break;
            }
            Cell s = baseCell;
            for (int i = 0; Math.Abs(i) < Math.Abs(Length); i += incr)
            {
                cell = cell.Worksheet[cell.Address.GetNeighbor(Direction)];
                cell.CopyFormattingRecord(s);

                if (s == lastCell)
                    s = baseCell;
                else
                    s = s.Worksheet[s.Address.GetNeighbor(Direction)];
            }
        }

        private void ApplyAutoFill(CellsRange targetRange, Cell baseCell, Cell lastCell, int length)
        {
            Contract.Requires(baseCell != null);
            Contract.Requires(lastCell != null);
            Expression expression = baseCell.Expression;
            if (expression != null)
            {
                CellsRange r = targetRange;
                r.ApplySharedFormula(baseCell.Formula);
                return;
            }
            Func<int, object> fn;
            fn = AutoFillFunctionBuilder.GetFunction(baseCell, lastCell, length, Direction);
            Cell c = lastCell;
            int incr;
            int cellIncr;
            switch (Direction)
            {
                case NavigationDirection.Right:
                case NavigationDirection.Down:
                    cellIncr = 1;
                    break;
                default:
                    cellIncr = -1;
                    break;
            }
            if (UseIncrement)
                incr = cellIncr;


            else
                incr = 0;

            for (int i = 0; Math.Abs(i) < Math.Abs(Length); i += cellIncr)
            {
                c = c.Worksheet[c.Address.GetNeighbor(Direction)];
                if (UseIncrement)
                    c.Value = fn(i + incr);
                else
                    c.Value = fn(0);
            }
        }

       
        

        private void applyAutoFill()
        {
            switch (Direction)
            {
                case NavigationDirection.Up:
                case NavigationDirection.Down:

                    for (int column = Source.Address.TopLeft.Column;
                         column <= Source.Address.BottomRight.Column;
                         column++)
                    {
                        ApplyAutoFill(
                            (CellsRange) TargetRange.GetColumnRange(column - Source.Address.TopLeft.Column, RangeType.Static),
                            Source.Worksheet[new CellAddress(Source.Address.TopLeft.Row, column)],
                            Source.Worksheet[new CellAddress(Source.Address.BottomRight.Row, column)],
                            Source.RowSpan);
                    }
                    break;
                case NavigationDirection.Left:
                case NavigationDirection.Right:

                    for (int row = Source.Address.TopLeft.Row; row <= Source.Address.BottomRight.Row; row++)
                    {
                        ApplyAutoFill(
                            (CellsRange) TargetRange.GetRowRange(row - Source.Address.TopLeft.Row, RangeType.Static),
                            Source.Worksheet[new CellAddress(row, Source.Address.TopLeft.Column)],
                            Source.Worksheet[new CellAddress(row, Source.Address.BottomRight.Column)],
                            Source.ColumnSpan
                            );
                    }
                    break;
            }
        }
    }
}